import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';

let scene, camera, renderer, controls, model;
const pickedPoints = []; // 存储用户选择的点
const MAX_PICKED_POINTS = 2; // 我们先实现两点间的距离测量
let pathLine; // 用于显示路径的Line对象

init();
animate();

function init() {
    // 1. 场景
    scene = new THREE.Scene();
    scene.background = new THREE.Color(0xeeeeee);

    // 2. 相机
    camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
    camera.position.set(2, 2, 5);

    // 3. 渲染器
    renderer = new THREE.WebGLRenderer({ antialias: true });
    renderer.setSize(window.innerWidth, window.innerHeight);
    document.body.appendChild(renderer.domElement);

    // 4. 光照
    const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
    scene.add(ambientLight);
    const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
    directionalLight.position.set(5, 5, 5);
    scene.add(directionalLight);

    // 5. 控制器
    controls = new OrbitControls(camera, renderer.domElement);

    // 6. 加载模型 (以GLTF为例)
    const loader = new GLTFLoader();
    loader.load(
        '/static/hamburger.glb', // 路径更新为相对于根目录
        function (gltf) {
            console.log(gltf);
            
            model = gltf.scene;
            // 确保模型居中并有合适的大小
            const box = new THREE.Box3().setFromObject(model);
            const center = box.getCenter(new THREE.Vector3());
            model.position.sub(center); // 居中
            const size = box.getSize(new THREE.Vector3());
            const maxDim = Math.max(size.x, size.y, size.z);
            const scale = 5 / maxDim; // 假设我们希望模型最大尺寸为5个单位
            model.scale.set(scale, scale, scale);

            scene.add(model);
            console.log("模型加载完成!");
            setupPicking(); // 模型加载后设置点选
        },
        undefined,
        function (error) {
            console.error('模型加载失败:', error);
        }
    );

    // 窗口大小调整响应
    window.addEventListener('resize', onWindowResize);
}

function onWindowResize() {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(window.innerWidth, window.innerHeight);
}

function animate() {
    requestAnimationFrame(animate);
    controls.update();
    renderer.render(scene, camera);
}

const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();
let pickingEnabled = false; // 控制是否可以拾取点

function setupPicking() {
    // 只有模型加载完成后才允许点选
    if (model) {
        pickingEnabled = true;
        console.log("可以开始在模型上选点了。");
    }
    window.addEventListener('click', onMouseClick);
}

function onMouseClick(event) {
    if (!pickingEnabled || !model) return;

    // 将鼠标点击位置转换为归一化设备坐标 (-1 到 +1)
    mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
    mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;

    // 通过相机和鼠标位置更新射线
    raycaster.setFromCamera(mouse, camera);

    // 计算物体和射线的交点 (只检测我们的模型)
    // true 表示检测所有后代对象
    const intersects = raycaster.intersectObject(model, true);

    if (intersects.length > 0) {
        const intersect = intersects[0]; // 取第一个交点
        const point = intersect.point; // 交点的世界坐标

        // 可视化选中的点
        const sphereGeometry = new THREE.SphereGeometry(0.05, 16, 16); // 根据模型大小调整球体大小
        const sphereMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000, depthTest: false }); // 修复为MeshBasicMaterial
        const pickedSphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
        pickedSphere.position.copy(point);
        scene.add(pickedSphere);

        pickedPoints.push({
            point: point, // 世界坐标
            face: intersect.face, // 相交的面
            object: intersect.object // 相交的对象 (可能是模型的子部分)
        });
        console.log("选中点:", point);

        if (pickedPoints.length === MAX_PICKED_POINTS) {
            console.log("已选择两个点，开始计算测地线距离。");
            calculateAndDrawGeodesicPath();
            pickingEnabled = false; // 暂时禁止继续选点，直到重置
        }
    }
}

function calculateAndDrawGeodesicPath() {
    if (pickedPoints.length < 2) return;

    const startPointData = pickedPoints[0];
    const endPointData = pickedPoints[1];

    // 我们需要访问模型的几何数据
    // 假设模型只有一个主要的Mesh，或者你需要遍历找到相关的Mesh
    let targetMesh;
    model.traverse((child) => {
        if (child.isMesh && child.geometry) {
            // 简单情况下，我们取第一个遇到的Mesh的geometry
            // 复杂模型可能需要更精确的定位，例如通过 intersect.object
            if (!targetMesh) targetMesh = child;
            // 如果startPointData.object 和 endPointData.object 是同一个，那更好
            if (child === startPointData.object) {
                 targetMesh = child;
            }
        }
    });

    if (!targetMesh || !targetMesh.geometry.isBufferGeometry) {
        console.error("无法获取有效的模型几何数据 (BufferGeometry)。");
        return;
    }

    const geometry = targetMesh.geometry;
    const vertices = geometry.attributes.position; // Vector3s in local space
    const index = geometry.index; // Indices for faces

    // 1. 将选择的世界坐标点转换为模型的局部坐标点，并找到最近的顶点
    // (这是一个简化，更精确的方法可能需要将选择点本身作为图的临时节点)
    const localStartPoint = targetMesh.worldToLocal(startPointData.point.clone());
    const localEndPoint = targetMesh.worldToLocal(endPointData.point.clone());

    const startVertexIndex = findClosestVertex(localStartPoint, vertices);
    const endVertexIndex = findClosestVertex(localEndPoint, vertices);

    if (startVertexIndex === -1 || endVertexIndex === -1) {
        console.error("无法找到最近的顶点。");
        return;
    }

    console.log(`起始顶点索引: ${startVertexIndex}, 目标顶点索引: ${endVertexIndex}`);

    // 2. 构建图 (邻接表)
    // graph[vertexIndex] = [{ neighborIndex, weight }, ...]
    const graph = buildGraphFromGeometry(geometry);

    // 3. 使用 Dijkstra 算法查找最短路径
    const dijkstraResult = dijkstra(graph, startVertexIndex, endVertexIndex, vertices.count);

    if (!dijkstraResult || dijkstraResult.path.length === 0) {
        console.error("未能找到路径。");
        // 可以尝试将选择点直接连接到其所在面的顶点，再进行路径查找
        alert("无法在选定顶点间找到表面路径。请尝试选择模型上连接性更好的点。");
        resetPicking();
        return;
    }

    const { path: vertexIndicesPath, distance: geodesicDistance } = dijkstraResult;
    console.log("测地线顶点索引路径:", vertexIndicesPath);
    console.log(`计算出的测地线距离: ${geodesicDistance.toFixed(4)} (模型局部单位)`);
    alert(`测地线距离: ${geodesicDistance.toFixed(4)} (模型局部单位)`);

    //
    if(!dijkstraResult || dijkstraResult.path.length === 0){
        return;
    }


    // 4. 可视化路径
    const actualStartWordPoint = pickedPoints[0].point.clone();
    const actualEndWordPoint = pickedPoints[pickedPoints.length - 1].point.clone();
    drawPath(vertexIndicesPath, vertices, targetMesh, actualStartWordPoint, actualEndWordPoint);

    // 允许用户重新选择
    resetPicking();
}

function findClosestVertex(point, positionAttribute) {
    let closestVertexIndex = -1;
    let minDistanceSq = Infinity;
    const vertex = new THREE.Vector3();

    for (let i = 0; i < positionAttribute.count; i++) {
        vertex.fromBufferAttribute(positionAttribute, i);
        const distanceSq = vertex.distanceToSquared(point);
        if (distanceSq < minDistanceSq) {
            minDistanceSq = distanceSq;
            closestVertexIndex = i;
        }
    }
    return closestVertexIndex;
}

function buildGraphFromGeometry(geometry) {
    const graph = {}; // 使用对象作为邻接表
    const positionAttribute = geometry.attributes.position;
    const indexAttribute = geometry.index;
    const tempVertexA = new THREE.Vector3();
    const tempVertexB = new THREE.Vector3();

    function addEdge(u, v) {
        tempVertexA.fromBufferAttribute(positionAttribute, u);
        tempVertexB.fromBufferAttribute(positionAttribute, v);
        const weight = tempVertexA.distanceTo(tempVertexB);

        if (!graph[u]) graph[u] = [];
        if (!graph[v]) graph[v] = [];

        // 避免重复添加边，并确保双向（如果是无向图）
        if (!graph[u].some(edge => edge.neighbor === v)) {
             graph[u].push({ neighbor: v, weight: weight });
        }
        if (!graph[v].some(edge => edge.neighbor === u)) {
            graph[v].push({ neighbor: u, weight: weight });
        }
    }

    if (indexAttribute) { // Indexed geometry (三角面)
        for (let i = 0; i < indexAttribute.count; i += 3) {
            const vA = indexAttribute.getX(i);
            const vB = indexAttribute.getX(i + 1);
            const vC = indexAttribute.getX(i + 2);
            addEdge(vA, vB);
            addEdge(vB, vC);
            addEdge(vC, vA);
        }
    } else { // Non-indexed geometry (顶点按顺序组成三角面)
        for (let i = 0; i < positionAttribute.count; i += 3) {
            const vA = i;
            const vB = i + 1;
            const vC = i + 2;
            addEdge(vA, vB);
            addEdge(vB, vC);
            addEdge(vC, vA);
        }
    }
    return graph;
}

// Dijkstra 算法实现
function dijkstra(graph, startNode, endNode, numVertices) {
    const distances = new Array(numVertices).fill(Infinity);
    const previousNodes = new Array(numVertices).fill(null);
    const pq = new PriorityQueue(); // 你需要一个优先队列的实现

    distances[startNode] = 0;
    pq.enqueue(startNode, 0);

    while (!pq.isEmpty()) {
        const { element: currentNode, priority: currentDistance } = pq.dequeue();

        if (currentNode === endNode) { // 找到最短路径
            const path = [];
            let curr = endNode;
            while (curr !== null) {
                path.unshift(curr);
                curr = previousNodes[curr];
            }
            return { path, distance: distances[endNode] };
        }

        if (currentDistance > distances[currentNode]) {
            continue; // 过时的条目
        }

        const neighbors = graph[currentNode] || [];
        for (const edge of neighbors) {
            const neighborNode = edge.neighbor;
            const weight = edge.weight;
            const distanceThroughCurrent = distances[currentNode] + weight;

            if (distanceThroughCurrent < distances[neighborNode]) {
                distances[neighborNode] = distanceThroughCurrent;
                previousNodes[neighborNode] = currentNode;
                pq.enqueue(neighborNode, distanceThroughCurrent);
            }
        }
    }
    return null; // 未找到路径
}

// 简单的优先队列实现 (用于Dijkstra)
class PriorityQueue {
    constructor() {
        this.items = [];
    }
    enqueue(element, priority) {
        const queueElement = { element, priority };
        let added = false;
        for (let i = 0; i < this.items.length; i++) {
            if (this.items[i].priority > queueElement.priority) {
                this.items.splice(i, 0, queueElement);
                added = true;
                break;
            }
        }
        if (!added) {
            this.items.push(queueElement);
        }
    }
    dequeue() {
        if (this.isEmpty()) return null;
        return this.items.shift();
    }
    isEmpty() {
        return this.items.length === 0;
    }
}

function drawPath(vertexIndices, positionAttribute, targetMesh, actualStartPoint, actualEndPoint) {
    // 1. 清理上一次的路径线
    if (pathLine) {
        scene.remove(pathLine);
        if (pathLine.geometry) pathLine.geometry.dispose();
        if (pathLine.material) pathLine.material.dispose();
        pathLine = null; // 重置变量
    }

    // 2. 将Dijkstra算法返回的顶点索引转换为世界坐标点
    const dijkstraWorldPoints = [];
    const tempVertex = new THREE.Vector3();
    if (vertexIndices && vertexIndices.length > 0) {
        for (const index of vertexIndices) {
            tempVertex.fromBufferAttribute(positionAttribute, index);
            // 将局部顶点坐标转换为世界坐标
            dijkstraWorldPoints.push(targetMesh.localToWorld(tempVertex.clone()));
        }
    }

    // 3. 构建最终用于绘制的路径点序列 (finalPathPoints)
    const pointsForLineConstruction = [];
    const minDistanceSq = 0.000001; // 用于判断点是否"相同"的距离平方阈值，根据模型尺度调整

    // 添加实际起点
    if (actualStartPoint) {
        pointsForLineConstruction.push(actualStartPoint.clone());
    }

    // 添加Dijkstra路径上的点
    for (const dPoint of dijkstraWorldPoints) {
        // 只有当列表为空，或者当前Dijkstra点与列表中最后一个点不同时才添加
        if (pointsForLineConstruction.length === 0 || 
            dPoint.distanceToSquared(pointsForLineConstruction[pointsForLineConstruction.length - 1]) > minDistanceSq) {
            pointsForLineConstruction.push(dPoint.clone());
        }
    }

    // 添加实际终点，并确保连接平滑
    if (actualEndPoint) {
        if (pointsForLineConstruction.length === 0) {
            // 如果列表仍为空（例如，Dijkstra路径为空且没有起点），直接添加终点
            pointsForLineConstruction.push(actualEndPoint.clone());
        } else {
            // 获取当前列表中的最后一个点（可能是实际起点，或Dijkstra路径的最后一个点）
            const lastCurrentPoint = pointsForLineConstruction[pointsForLineConstruction.length - 1];
            if (actualEndPoint.distanceToSquared(lastCurrentPoint) <= minDistanceSq) {
                // 如果实际终点与列表中的最后一个点非常接近（几乎是同一个点），
                // 就用实际终点的精确坐标替换掉列表中的最后一个点。
                // 这确保了路径在 actualEndPoint 的精确位置结束。
                pointsForLineConstruction[pointsForLineConstruction.length - 1] = actualEndPoint.clone();
            } else {
                // 如果实际终点与列表中的最后一个点有明显距离，则将其作为新的点添加到列表末尾。
                pointsForLineConstruction.push(actualEndPoint.clone());
            }
        }
    }

    // 4. 清理最终路径点：移除可能因上述逻辑产生的连续重复点（作为保险措施）
    const finalPathPoints = [];
    if (pointsForLineConstruction.length > 0) {
        finalPathPoints.push(pointsForLineConstruction[0].clone()); // 添加第一个点
        for (let i = 1; i < pointsForLineConstruction.length; i++) {
            if (pointsForLineConstruction[i].distanceToSquared(finalPathPoints[finalPathPoints.length - 1]) > minDistanceSq) {
                finalPathPoints.push(pointsForLineConstruction[i].clone());
            }
        }
    }
    
    // 5. 如果最终路径点少于2个，则无法形成线段
    if (finalPathPoints.length < 2) {
        console.warn("最终路径点不足2个，无法形成线段。清理前点:", pointsForLineConstruction, "清理后点:", finalPathPoints);
        // 确保如果之前有线，它已经被移除了
        if (pathLine && pathLine.parent) scene.remove(pathLine);
        return;
    }

    // 6. 创建并显示新的路径线
    const pathGeometry = new THREE.BufferGeometry().setFromPoints(finalPathPoints);
    const pathMaterial = new THREE.LineBasicMaterial({ color: 0x0000ff, linewidth: 3, depthTest: false });
    pathLine = new THREE.Line(pathGeometry, pathMaterial);
    scene.add(pathLine);

    // 7. 计算并打印可视化路径的总长度
    let visualPathLength = 0;
    for (let i = 0; i < finalPathPoints.length - 1; i++) {
        visualPathLength += finalPathPoints[i].distanceTo(finalPathPoints[i + 1]);
    }
    console.log(`可视化路径的总长度 (世界单位): ${visualPathLength.toFixed(4)}`);
}

function resetPicking() {
    pickedPoints.length = 0; // 清空数组
    pickingEnabled = true;
    console.log("可以重新选点了。");

    // 如果需要移除路径线，可以在这里做，或者在下一次画线前移除
    if (pathLine) {
         // scene.remove(pathLine); // 可以选择在下一次绘制前清除
         // pathLine.geometry.dispose();
         // pathLine.material.dispose();
         // pathLine = null;
    }
} 